/*
 * Here is defined sbrk.
 * Sbrk takes @delta as parameter, which is the difference of size that the
 * pool should have since the previous sbrk call.
 * Initially the pool as a certain size, so that we can call malloc/valloc/etc,
 * before initializing paging. After the maximum size has been reached, a new
 * page have to be mapped.
 */

#include <arch/types.h>
#include <arch/CPU.h>
#include <arch/Paging.h>
#include <kernel/Memory.h>
#include <kernel/Panic.h>
#include <stdio.h>
#include <string.h>

#define KHEAP_ADDR	0xC0420000
#define KHEAP_END	0xF0000000
#define KHEAP_SIZE	(KHEAP_END - KHEAP_ADDR)
#define KHEAP_INIT	(0xC0800000 - KHEAP_ADDR)

/* The last set break */
static void *KHeapBrk = (void *) KHEAP_ADDR;
/* The last address in the pool */
static dword KHeapTop = KHEAP_ADDR + KHEAP_INIT;

void *sbrk(long delta) {
	dword frame;
	void *old = KHeapBrk;

	if (delta < 0) {
		printf("WARNING, sbrk with delta < 0!!! To be tested\n");
		while (delta % 0x1000) {
			CPUs[0]->PageDir->UnmapMemory(KHeapTop - 0x1000, 0x1000);
			Memory.Free(KHeapTop - 0x1000, 1);
			KHeapTop -= 0x1000;
		}
	}
	
	if (!delta)
		return KHeapBrk;

	if ((dword) KHeapBrk + delta >= KHEAP_END) {
		panic("End of kernel memory!");
	}

	while ((dword) KHeapBrk + delta >= KHeapTop) {
		frame = Memory.Alloc(0x100000, 1);
		if (!frame)
			panic("End of kernel memory (frame allocation failed)!");
		CPUs[0]->PageDir->MapMemory(PAGE_FLAGS_WRITE | PAGE_FLAGS_READ, KHeapTop, frame, 0x1000);
		memset((void *) KHeapTop, 0, 0x1000);
		KHeapTop += 0x1000;
	}

	KHeapBrk = (void *) ((dword) KHeapBrk + delta);
	return old;
}

